package com.jketing.service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Stack;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaBuilder.In;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import com.jketing.exception.SyntacticErrorException;

/**
 *	PredicateBuilder   jpa 查询条件构建器
 * 
 *	@author  李荣华
 *	@version v1.0
 *	@date    2017-11-10
 *	@since   jdk1.5
 */
@Service
public class PredicateBuilder<T> {
	/**
	 * ops 运算符栈 .
	 */
	private Stack<Character> ops = new Stack<Character>();
	/**
	 * opds 查询结构操作数栈
	 */
	private Stack<Predicate> opds = new Stack<Predicate>();
	
	/**
	 * isDelimiter 判断一个字符是否为分隔符 
	 * 表达式中的字符包括： 
	 * 与:'!' 或:'｜' 非:'!' 异或:'^' 左小括号:'(' 右小括号:')'
	 */
	private boolean isDelimiter(char c) {
		if (("!|&^()#".indexOf(c) != -1))
			return true;
		return false;
	}
	
	/**
	 *  relation 	查表返回运算符优先级
	 *  
	 *  @param  	char left 左字符
	 *  @param  	char right 右字符
	 *  
	 *  @return		char 结果取值:'>','<','=','x'
	 */
	private char relation(char left,char right) {
		char[] ch = {left,right};
		int[] index = {-1,-1};
		char[][] re = {
				{'>','>','>','>','<','>','>'},
				{'<','>','>','>','<','>','>'},
				{'<','<','>','>','<','>','>'},
				{'<','<','>','>','<','>','>'},
				{'<','<','<','<','<','=','x'},
				{'>','>','>','>','x','>','>'},
				{'<','<','<','<','<','x','='}
		};
		for(int i=0;i<=1;i++) {
			switch(ch[i]) {
				case'!':index[i]=0;break;
				case'&':index[i]=1;break;
				case'|':index[i]=2;break;
				case'^':index[i]=3;break;
				case'(':index[i]=4;break;
				case')':index[i]=5;break;
				case'#':index[i]=6;break;
				default://出错，抛异常
			}
		}
		return re[index[0]][index[1]];
	}
	
	/**
	 *  getPredicateForString 得到字符串类型的查询结构
	 *  
	 *  @param	Expression<String> expression jpa字符串查询表达式
	 *  @param	String op 查询操作符
	 *  @param	List<String> list 用于构建查询的字符串值列表，表单提交的数据
	 *  @param 	CriteriaBuilder cb jpa查询条件构建器
	 *  
	 *  @return Predicate	返回jpa查询结构
	 */
	private Predicate getPredicateForString(Expression<String> expression,String op,List<String> list,CriteriaBuilder cb){
		Predicate predicate = null;
		String like = null;
		switch(op) {
		case "=": 
			predicate = cb.equal(expression, list.get(0));
			break;
		case ">": 
			predicate = cb.greaterThan(expression, list.get(0));
			break;
		case ">=": 
			predicate = cb.greaterThanOrEqualTo(expression, list.get(0));
			break;
		case "<": 
			predicate = cb.lessThan(expression, list.get(0));
			break;
		case "<=": 
			predicate = cb.lessThanOrEqualTo(expression, list.get(0));
			break;
		case "<>": 
			predicate = cb.notEqual(expression, list.get(0));
			break;
		case "between": 
			predicate = cb.between(expression, list.get(0), list.get(1));
			break;
		case "not between": 
			predicate = cb.not(cb.between(expression, list.get(0), list.get(1)));
			break;
		case "X%": 
			if (list.get(0).indexOf("%") < 0)  
				like = list.get(0) + "%";  
			predicate = cb.like(expression, like); 
			break;
		case "%X": 
			if (list.get(0).indexOf("%") < 0)  
				like = "%" + list.get(0);  
			predicate = cb.like(expression, like);  
			break;
		case "%X%": 
			if (list.get(0).indexOf("%") < 0)  
				like = "%" + list.get(0) + "%";  
			predicate = cb.like(expression, like);  
			break;
		case "~X%": 
			if (list.get(0).indexOf("%") < 0)  
				like = list.get(0) + "%"; 
			predicate = cb.notLike(expression, like); 
			break;
		case "~%X": 
			if (list.get(0).indexOf("%") < 0)  
				like = list.get(0) + "%"; 
			predicate = cb.notLike(expression, like); 
			break;
		case "~%X%": 
			if (list.get(0).indexOf("%") < 0)  
				like = "%" + list.get(0) + "%";
			predicate = cb.notLike(expression, like); 
			break;	
		case "in":
	        In<String> in = cb.in(expression);  
	        while (list.iterator().hasNext()) {  
	            in.value(list.iterator().next());  
	        }  
			predicate = in; 
			break;	
		case "~in":
	        in = cb.in(expression);  
	        while (list.iterator().hasNext()) {  
	            in.value(list.iterator().next());  
	        }  
			predicate = cb.not(in); 
			break;	
		case "isNull": 
			predicate = cb.isNull(expression);
			break;
		case "isNotNull": 
			predicate = cb.isNotNull(expression);
			break;
		case "all": //找在子查询所有结果都满足的
			//Subquery<MessageWrapper> subquery = criteriaQuery.subquery(MessageWrapper.class);
		    //Root<Message> subRoot = subquery.from(Message.class);
		    //subquery.select(cb.construct(MessageWrapper.class, subRoot.get(Message_.chain), cb.max(subRoot.get(Message_.id))));
			//predicate = cb.all(expression);
			break;
		case "any": ////找在子查询任何一个结果都满足的
			break;
		case "some": ////找在子查询任何一个结果都满足的
			break;
		case "exists": ////在子查询结果中是否存在
			break;
		case "not exists": ////在子查询结果中是否不存在
			break;
	}
		return predicate;
	}
	
	/**
	 *  getPredicateForNumber 得到数值类型的查询结构
	 *  
	 *  @param	Expression<Number> expression jpa数值查询表达式
	 *  @param	String op 查询操作符
	 *  @param	List<Number> list 查询的数值值列表
	 *  @param 	CriteriaBuilder cb jpa查询条件构建器
	 *  
	 *  @return Predicate	返回jpa查询结构
	 */
	Predicate getPredicateForNumber(Expression<Number> expression,String op,List<Number> list,CriteriaBuilder cb){
		Predicate predicate = null;
		switch(op) {
		case "=": 
			predicate = cb.equal(expression, list.get(0));
			break;
		case ">": 
			predicate = cb.gt(expression, list.get(0));
			break;
		case ">=": 
			predicate = cb.ge(expression, list.get(0));
			break;
		case "<": 
			predicate = cb.lt(expression, list.get(0));
			break;
		case "<=": 
			predicate = cb.le(expression, list.get(0));
			break;
		case "<>": 
			predicate = cb.notEqual(expression, list.get(0));
			break;
		case "between": 
			predicate = cb.and(cb.ge(expression, list.get(0)), cb.le(expression, list.get(1)));
			break;
		case "not_between": 
			predicate = cb.not(cb.and(cb.ge(expression, list.get(0)), cb.le(expression, list.get(1))));
			break;
		case "in":
	        In<Number> in = cb.in(expression);  
	        while (list.iterator().hasNext()) {  
	            in.value(list.iterator().next());  
	        }  
			predicate = in; 
			break;	
		case "~in": 
	        in = cb.in(expression);  
	        while (list.iterator().hasNext()) {  
	            in.value(list.iterator().next());  
	        }  
			predicate = cb.not(in); 
			break;	
		case "isNull": 
			predicate = cb.isNull(expression);
			break;
		case "isNotNull": 
			predicate = cb.isNotNull(expression);
			break;
		case "all": //找在子查询所有结果都满足的
			//Subquery<MessageWrapper> subquery = criteriaQuery.subquery(MessageWrapper.class);
		    //Root<Message> subRoot = subquery.from(Message.class);
		    //subquery.select(cb.construct(MessageWrapper.class, subRoot.get(Message_.chain), cb.max(subRoot.get(Message_.id))));
			//predicate = cb.all(expression);
			break;
		case "any": ////找在子查询任何一个结果都满足的
			break;
		case "some": ////找在子查询任何一个结果都满足的
			break;
		case "exists": ////在子查询结果中是否存在
			break;
		case "not exists": ////在子查询结果中是否不存在
			break;
		}
		return predicate;
	}
	
	/**
	 *  getPredicateForDate 得到日期类型的查询结构
	 *  
	 *  @param	Expression<Date> expression jpa日期查询表达式
	 *  @param	String op 查询操作符
	 *  @param	List<Date> list 用于构建查询的日期值列表
	 *  @param 	CriteriaBuilder cb jpa查询条件构建器
	 *  
	 *  @return Predicate	返回jpa查询结构
	 */
	private Predicate getPredicateForDate(Expression<Date> expression,String op,List<Date> list,CriteriaBuilder cb){
		Predicate predicate = null;
		switch(op) {
		case "=": 
			predicate = cb.equal(expression, list.get(0));
			break;
		case ">": 
			predicate = cb.greaterThan(expression, list.get(0));
			break;
		case ">=": 
			predicate = cb.greaterThanOrEqualTo(expression, list.get(0));
			break;
		case "<": 
			predicate = cb.lessThan(expression, list.get(0));
			break;
		case "<=": 
			predicate = cb.lessThanOrEqualTo(expression, list.get(0));
			break;
		case "<>": 
			predicate = cb.notEqual(expression, list.get(0));
			break;
		case "between": 
			predicate = cb.between(expression, list.get(0), list.get(1));
			break;
		case "not between": 
			predicate = cb.not(cb.between(expression, list.get(0), list.get(1)));
			break;
		case "isNull": 
			predicate = cb.isNull(expression);
			break;
		case "isNotNull": 
			predicate = cb.isNotNull(expression);
			break;
		case "in":
	        In<Date> in = cb.in(expression);  
	        while (list.iterator().hasNext()) {  
	            in.value(list.iterator().next());  
	        }  
			predicate = in; 
			break;	
		case "~in": 
	        in = cb.in(expression);  
	        while (list.iterator().hasNext()) {  
	            in.value(list.iterator().next());  
	        }  
			predicate = cb.not(in); 
			break;	
		case "all": //找在子查询所有结果都满足的
			//Subquery<MessageWrapper> subquery = criteriaQuery.subquery(MessageWrapper.class);
		    //Root<Message> subRoot = subquery.from(Message.class);
		    //subquery.select(cb.construct(MessageWrapper.class, subRoot.get(Message_.chain), cb.max(subRoot.get(Message_.id))));
			//predicate = cb.all(expression);
			break;
		case "any": ////找在子查询任何一个结果都满足的
			break;
		case "some": ////找在子查询任何一个结果都满足的
			break;
		case "exists": ////在子查询结果中是否存在
			break;
		case "not exists": ////在子查询结果中是否不存在
			break;
		}
		return predicate;
	}
	
	/**
	 *  queryEntityBuilder 
	 *  
	 *  第二级以上实体查询构建器，递归支持无限级联属性
	 *  
	 *  @param	String query 查询字符串表达式
	 *  @param	Path<T> path jpa查询构建路径
	 *  @param 	CriteriaBuilder cb jpa查询条件构建器
	 *  
	 *  @return Predicate	返回jpa查询结构
	 */
	Predicate queryEntityBuilder (String query,Path<T> path,CriteriaBuilder cb) throws ParseException, ClassNotFoundException {
		Predicate predicate = null;
		String[] queries = query.split(":");//形如：属性类型:属性名:运算符:属性值
		if(!"Entity".equals(queries[0])) {
			String[] values = queries[3].split(",");//queries[3]属性值
			switch(queries[0]) {//queries[0]属性类型
				case "String":
					List<String> list = Arrays.asList(values);
					predicate = getPredicateForString(path.get(queries[1]).as(String.class),queries[2],list,cb);//queries[2]查询运算符
					break;
				case "Date": 
					List<Date> dates = new ArrayList<Date>();
					SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");					
					for (String x : values) {
						dates.add(df.parse(x));
			        }
					predicate = getPredicateForDate(path.get(queries[1]).as(Date.class),queries[2],dates,cb);
					break;
				case "Byte":
				case "Short": 
				case "Integer": 
				case "Long":
				case "Float":
				case "Double":
					List<Number> vals = new ArrayList<Number>();
					for (String x : values) {
						vals.add(Double.parseDouble(x));
			        }
					predicate = getPredicateForNumber(path.get(queries[1]).as(Number.class),queries[2],vals,cb);
					break;
				case "Boolean": 
					break;
				case "Character":
					break;				
				default:
			}
		}else {
			path = path.get(queries[1]);
			for(int i=0;i<2;i++) {
				int index = query.indexOf(":");
				query = query.substring(++index);
			}
			predicate = queryEntityBuilder(query,path,cb);//递归生成引用实体的属性查询
		}
		System.out.println("++++++++++++" + predicate.toString() +"++++++++++++++");
		return predicate;
	}
	
	/**
	 *  queryEntityBuilder 
	 *  
	 *  第一级实体查询构建器
	 *  
	 *  如:按部门查员工，员工和部门是多对多关系
	 *  当前employee是root
	 *  表达写法：
	 *  两级联表查询 Entity:organes:String:id:=:PK00000000000001  
	 *  三级联表查询 Entity:organes:Entity:address:String:post:=:654200 
	 *  注：按员工所在部门地址的邮编查询，邮编值为654200，员工引用部门实体，部门引用地址实体，邮编为普通属性
	 *  四级联表查询 Entity:organes:Entity:address:Entity:city:String:zone:=:五华区
	 *  注：按员工所在部门的地址，地址的城市，城市的区查询，区值为五华区，员工引用部门实体，部门引用地址实体，地址引用城市实体，区为普通属性
	 *   
	 *  @param	String query 查询字符串表达式
	 *  @param	Join<T,?> join jpa联表查询join路径构建
	 *  @param 	CriteriaBuilder cb jpa查询条件构建器
	 *  
	 *  @return Predicate	返回jpa查询结构
	 */
	Predicate queryEntityBuilder (String query,Join<T,?> join,CriteriaBuilder cb) throws ParseException, ClassNotFoundException {
		Predicate predicate = null;
		String[] queries = query.split(":");//形如：属性类型:属性名:运算符:属性值
		if(!"Entity".equals(queries[0])) {
			String[] values = queries[3].split(",");//queries[3]属性值
			switch(queries[0]) {//queries[0]属性类型
				case "String":
					List<String> list = Arrays.asList(values);
					predicate = getPredicateForString(join.get(queries[1]).as(String.class),queries[2],list,cb);//queries[2]查询运算符
					break;
				case "Date": 
					List<Date> dates = new ArrayList<Date>();
					SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");					
					for (String x : values) {
						dates.add(df.parse(x));
			        }
					predicate = getPredicateForDate(join.get(queries[1]).as(Date.class),queries[2],dates,cb);
					break;
				case "Byte":
				case "Short": 
				case "Integer": 
				case "Long":
				case "Float":
				case "Double":
					List<Number> vals = new ArrayList<Number>();
					for (String x : values) {
						vals.add(Double.parseDouble(x));
			        }
					predicate = getPredicateForNumber(join.get(queries[1]).as(Number.class),queries[2],vals,cb);
					break;
				case "Boolean": 
					break;
				case "Character":
					break;				
				default:
			}
		}else {
			Path<T> path = join.get(queries[1]);
			for(int i=0;i<2;i++) {
				int index = query.indexOf(":");
				query = query.substring(++index);
			}
			predicate = queryEntityBuilder(query,path,cb);//递归生成引用实体的属性查询
		}
		System.out.println("++++++++++++" + predicate.toString() +"++++++++++++++");
		return predicate;
	}
	
	/**
	 *  queryBuilder 单条件查询构建器
	 *   
	 *  @param	String query 查询字符串表达式
	 *  @param	Root<T> root jpa查询根路径
	 *  @param 	CriteriaBuilder cb jpa查询条件构建器
	 *  
	 *  @return Predicate	返回jpa查询结构
	 */
	Predicate queryBuilder (String query,Root<T> root,CriteriaBuilder cb) throws ParseException, ClassNotFoundException {
		Predicate predicate = null;
		if(StringUtils.isNotBlank(query)){
			String[] queries = query.split(":");//形如：属性类型:属性名:运算符:属性值
			if(!"Entity".equals(queries[0])) {
				Path<T> path = root.get(queries[1]);//queries[1]属性名
				String[] values = queries[3].split(",");//queries[3]属性值
				switch(queries[0]) {//queries[0]属性类型
					case "String":
						List<String> list = Arrays.asList(values);
						predicate = getPredicateForString(path.as(String.class),queries[2],list,cb);//queries[2]查询运算符
						break;
					case "Date": 
						List<Date> dates = new ArrayList<Date>();
						SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");					
						for (String x : values) {
							dates.add(df.parse(x));
				        }
						predicate = getPredicateForDate(path.as(Date.class),queries[2],dates,cb);
						break;
					case "Byte":
					case "Short": 
					case "Integer": 
					case "Long":
					case "Float":
					case "Double":
						List<Number> vals = new ArrayList<Number>();
						for (String x : values) {
							vals.add(Double.parseDouble(x));
				        }
						predicate = getPredicateForNumber(path.as(Number.class),queries[2],vals,cb);
						break;
					case "Boolean": 
						break;
					case "Character":
						break;				
					default:
				}
			}else {
				//实体类型固定格式:Entity:organSet:String:id:=:PK000000001
				//实体查询标志:实体引用名:属性类型:属性名:运算符:属性值
				//三级联表可写成  实体查询标志:实体引用名:实体查询标志:实体引用名:实体查询标志:实体引用名:属性类型:属性名:运算符:属性值,支持无限级
				//String prefix = "com.jketing.entity.";
				//String className = prefix + queries[1];
				//Class<?> c = Class.forName(className);
				//Join<T,?> join = root.join(root.getModel().getSingularAttribute(queries[2],c),JoinType.LEFT);//报错
				Join<T,?> join = root.join(queries[1],JoinType.LEFT);
				//Path<T> path = join.get(property[2]);//queries[1]实体引用名
				for(int i=0;i<2;i++) {
					int index = query.indexOf(":");
					query = query.substring(++index);
				}
				predicate = queryEntityBuilder(query,join,cb);//生成引用实体的属性查询
			}
			System.out.println("++++++++++++" + predicate.toString() +"++++++++++++++");
		}
		return predicate;
	}
	
	/**
	 *  queryBuilder 复合条件查询构建器
	 *   
	 *  @param	Predicate left 左条件结构
	 *  @param	Predicate right 右条件结构
	 *  @param 	CriteriaBuilder cb jpa查询条件构建器
	 *  
	 *  @return Predicate	返回jpa查询结构
	 */
	Predicate queryBuilder (Predicate left,char c,Predicate right,CriteriaBuilder cb) {
		Predicate result = null;
		switch(c) {
		case'!':
			result = cb.not(right);
			break;
		case'&':
			result = cb.and(left, right);
			break;
		case'|':
			result = cb.or(left, right);
			break;
		case'^':
			result = cb.or(cb.and(cb.not(left),right),cb.and(left,cb.not(right)));
			break;
		default://出错，抛异常
		}
		System.out.println("------" + result.toString() +"-------");
		return result;
	}
	
	void operate(char ops_peek,char current,CriteriaBuilder cb) throws SyntacticErrorException{
		char temp = relation(ops_peek,current);
		char ops_top;
		switch(temp) {
		case'<':
			ops.push(current);
			break;
		case'=':
			ops_top = ops.pop();
			break;
		case'>':
			ops_top = ops.pop();
			Predicate left,right;
			if(ops_top=='!') {
				right = opds.pop();
				left = null;
			}else {
				right = opds.pop();
				left = opds.pop();
			}
			opds.push(queryBuilder(left,ops_top,right,cb));
			if(current==')') {
				if(ops.peek()!='(') {
					operate(ops.peek(),current,cb);//递归比较栈内运算符，直到左括号为止
				}else {
					ops.pop();//废去左括号
				}
			}else if(current!='#'){
				operate(ops.peek(),current,cb);//递归比较栈内运算符，直到current<ops.peek入栈为止
			}
			break;
		case'x':
			throw new SyntacticErrorException("表达式语法错误：" + ops_peek + current);
		}
	}
	
	/**
	 *	getPredicateByExpression	
	 *
	 *	解析逻辑表达式得到查询条件
	 * 
	 *	功能:
	 *	为实现where子句动态逻辑组合查询
	 *	表单提交形如:
	 *	"[q1] | [q2] & [q3] | ([q4] | [q5]) | [q6] & ![q7] ^ [q8]#"表达式
	 *	运算符对应：
	 *	qx:代有一个where子句查询，如：name link '%李%',age>23等  
	 *	&:and 与
	 *  |:or  或
	 *  !:not 非
	 *  ^:xor 异或
	 *  #:end 结束标志
	 * 
	 *	@param  	String expression 逻辑表达式
	 *  @param		Root<T> root  查询根路径(保存查询属性名及类型)
	 *  @param		CriteriaBuilder cb jpa查询构建器
	 *  
	 *  @return		Predicate	查询条件
	 *  
	 *  @throws ParseException ClassNotFoundException 
	 * @throws SyntacticErrorException 
	 */
	public Predicate getPredicateByExpression(String expression,
													 Root<T> root,
													 CriteriaBuilder cb) 
															 throws ParseException, ClassNotFoundException, SyntacticErrorException{
		ops.push('#');
		int i = 0;
		char c = expression.charAt(i);		
		while(c != '#'||ops.peek()!='#') {
			if(isDelimiter(c)) {
				operate(ops.peek(),c,cb);
			}else {
				if(c=='[') {
					// 如果当前字符是一个[，则认为是一个查询量开始标记
					// 将解析器指针往后移，知道遇到另一个]分隔符，之间的字符都是查询量的组成部分
					StringBuilder sb = new StringBuilder();
					c = expression.charAt(++i);
					while (c!=']') {
						sb.append(c);
						c = expression.charAt(++i);
					}
					opds.push(queryBuilder(sb.toString(),root,cb));
				}
			}
			if(++i <  expression.length()) c = expression.charAt(i);
		}
		System.out.println("++++++++++++++++++++++++++++++reuslt : " + opds.peek());
		return opds.pop();
	}
}

